package com.ayf.payment.game.api.httpclient;

import com.ayf.payment.game.api.utils.Base64;
import com.swwx.charm.commons.lang.utils.LogPortal;

import javax.crypto.Cipher;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;


public class RSA {

    public static String ALGORITHM = "RSA";

    public static String SIGN_ALGORITHMS = "SHA256WithRSA";// 摘要加密算饭

    private static String log = "RSAUtil";

    public static String CHAR_SET = "UTF-8";

    /**
     * 数据签名
     *
     * @param content    签名内容
     * @param privateKey 私钥
     * @return 返回签名数据
     */
    public static String sign(String content, String privateKey) {
        try {
            PKCS8EncodedKeySpec priPKCS8 = new PKCS8EncodedKeySpec(
                    Base64.decode(privateKey));
            KeyFactory keyf = KeyFactory.getInstance("RSA");
            PrivateKey priKey = keyf.generatePrivate(priPKCS8);

            java.security.Signature signature = java.security.Signature
                    .getInstance(SIGN_ALGORITHMS);

            signature.initSign(priKey);
            signature.update(content.getBytes(CHAR_SET));

            byte[] signed = signature.sign();

            return Base64.encode(signed);
        } catch (Exception e) {
            LogPortal.error("签名异常：{}", e, e.getMessage());
        }

        return null;
    }

    /**
     * 签名验证
     *
     * @param content
     * @param sign
     * @param public_key
     * @return
     */
    public static boolean verify(String content, String sign,
                                 String public_key) {
        try {
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            byte[] encodedKey = Base64.decode(public_key);
            PublicKey pubKey = keyFactory.generatePublic(new X509EncodedKeySpec(encodedKey));

            java.security.Signature signature = java.security.Signature.getInstance(SIGN_ALGORITHMS);

            signature.initVerify(pubKey);
            signature.update(content.getBytes(CHAR_SET));

			return signature.verify(Base64.decode(sign));

        } catch (Exception e) {
            e.printStackTrace();
        }

        return false;
    }

    /**
     * 通过公钥解密
     *
     * @param content 待解密数据
     * @param pk      公钥
     * @return 返回 解密后的数据
     */
    protected static byte[] decryptByPublicKey(String content, PublicKey pk) {

        try {
            Cipher ch = Cipher.getInstance(ALGORITHM);
            ch.init(Cipher.DECRYPT_MODE, pk);
            InputStream ins = new ByteArrayInputStream(Base64.decode(content));
            ByteArrayOutputStream writer = new ByteArrayOutputStream();
            // rsa解密的字节大小最多是256，将需要解密的内容，按256位拆开解密
            byte[] buf = new byte[256];
            int bufl;
            while ((bufl = ins.read(buf)) != -1) {
                byte[] block = null;

                if (buf.length == bufl) {
                    block = buf;
                } else {
                    block = new byte[bufl];
					System.arraycopy(buf, 0, block, 0, bufl);
                }

                writer.write(ch.doFinal(block));
            }

            return writer.toByteArray();

        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;

    }

    /**
     * 通过私钥加密
     *
     * @param content
     * @param pk
     * @return 加密数据，未进行base64进行加密
     */
    protected static byte[] encryptByPrivateKey(String content, PrivateKey pk) {

        try {
            Cipher ch = Cipher.getInstance(ALGORITHM);
            ch.init(Cipher.ENCRYPT_MODE, pk);
            return ch.doFinal(content.getBytes(CHAR_SET));
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("通过私钥加密出错");
        }
        return null;

    }

    /**
     * 解密数据，接收端接收到数据直接解密
     *
     * @param content
     * @param publicKey
     * @return
     */
    public static String decrypt(String content, String publicKey) {
        if (null == publicKey || "".equals(publicKey)) {
            System.out.println(log + "：decrypt方法中key=" + publicKey);
            return null;
        }
        PublicKey pk = getPublicKey(publicKey);
        byte[] data = decryptByPublicKey(content, pk);
        String res = null;
        try {
			assert data != null;
			res = new String(data, CHAR_SET);
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return res;
    }

    /**
     * 对内容进行加密
     *
     * @param content
     * @param privateKey 私钥
     * @return
     */
    public static String encrypt(String content, String privateKey) {
        PrivateKey pk = getPrivateKey(privateKey);
        byte[] data = encryptByPrivateKey(content, pk);
        String res = null;
        try {
            res = Base64.encode(data);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return res;

    }

    /**
     * 得到私钥对象
     *
     * @param privateKey 密钥字符串（经过base64编码的秘钥字节）
     * @throws Exception
     */
    public static PrivateKey getPrivateKey(String privateKey) {
        try {
            byte[] keyBytes;

            keyBytes = Base64.decode(privateKey);

            PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);

            KeyFactory keyFactory = KeyFactory.getInstance("RSA");

			return keyFactory.generatePrivate(keySpec);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }


    /**
     * 获取公钥对象
     *
     * @param publicKey 密钥字符串（经过base64编码秘钥字节）
     * @throws Exception
     */
    public static PublicKey getPublicKey(String publicKey) {

        try {

            byte[] keyBytes;

            keyBytes = Base64.decode(publicKey);

            X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);

            KeyFactory keyFactory = KeyFactory.getInstance("RSA");

			return keyFactory.generatePublic(keySpec);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;

    }
}
